#include "my.h"
typedef struct
{
	int type;  //请求的类型
	char username[50];
	char passwd[50];
	
	char filename[50];	//保存文件名
	char filedata[1000];	//保存文件内容
	int len;
}data_t;

sqlite3 *db = NULL;//将句柄定义为全局变量

void mysqlite3_exec(const char *sql)
{
	char *errmsg = NULL;
	int ret2 = sqlite3_exec(db, sql, NULL, NULL, &errmsg);
	if(ret2 != 0)
	{
		printf("sqlite3_exec failed: %s\n", sqlite3_errmsg(db));
		exit(-1);
	}
	printf("成功\n");

}
int mySqlite3GetTable(const char *sql)
{
	char **resultp = NULL;
	int row = 0,column,i;
	char *errmsg = NULL;
	int ret = sqlite3_get_table(db, sql, &resultp, &row, &column, &errmsg);
	if(ret != 0)
	{
		printf("sqlite3_get_table failed:%s\n",errmsg);
		exit(-1);
	}

	return row; //将查询结果的行数返回
}


void createDir(data_t s)
{
	pid_t ret = fork();
	if(ret == -1)
	{
		perror("fork failed");
		exit(-1);
	}
	else if(ret == 0)
	{
		char buf[100] = { 0 };
		sprintf(buf, "./share/%s",s.username);// mkdir ./share/zhangsan
		execlp("mkdir","mkdir", buf, NULL);
	}
	else if(ret > 0)
	{
		wait(NULL);//回收子进程的资源，避免僵尸进程
		printf("创建私人网盘%s目录完毕!!\n",s.username);
	}

}
void sqlite3_create()
{
	
	int ret = sqlite3_open("./my.db",&db);
	if(ret != 0)
	{
		printf("sqlite3_open failed\n");
		exit(-1);
	}
	printf("打开数据库文件成功\n");


	char sql[200] = "create table if not exists user_info (username varchar,passwd varchar)";
	mysqlite3_exec(sql);
	sprintf(sql,"create table if not exists his_info(username varchar(20),time varchar(50),something varchar)");
	mysqlite3_exec(sql);
}

//处理注册请求的函数
void dealRegister(data_t s, int newsockfd)
{
	char sql[200] = { 0 };
	//1.去数据库中的user_info表中去校验，判断用户名是否存在
	sprintf(sql,"select * from user_info where username = '%s'", s.username);
	int row = mySqlite3GetTable(sql);
	//2.对查询结果进行判断
	if(row == 0)//说明用户名在数据库中不存在，允许被注册
	{
		//2.1将用户名和密码保存到数据库user_info表中
		sprintf(sql,"insert into user_info values('%s', '%s')", s.username, s.passwd);
		mysqlite3_exec (sql);
		//2.2用该用户的用户名创建一个文件夹做为该用户的私人网盘
		createDir(s);
		s.type = 0;//用0来代表注册成功
	}
	else//用户名在数据库存在，本次注册失败
	{
		s.type = -1;//用-1来代表注册失败
	}
	//3.将注册结果发送给客户端
	send(newsockfd, &s, sizeof(s), 0);

}

void login(int newsockfd,data_t s)
{
	//1.去数据库中校验用户名和密码
	char sql[200] = { 0 };
	sprintf(sql,"select * from user_info where username = '%s'",s.username);
	int row = mySqlite3GetTable(sql);
	if(row == 0)//说明用户名不存在，错误原因就是用户名不存在
		s.type = -1;//代表登录失败原因是用户名不存在
	else//说明用户名存在，继续判断用户密码是否正确
	{
		sprintf(sql,"select * from user_info where username = '%s' and passwd = '%s'",s.username,s.passwd);
		int row2 = mySqlite3GetTable(sql);
		if(row2 == 0)//说明密码不正确
			s.type = -2;//代表登录失败的原因是密码错误
		else
			s.type = 0;//说明登录成功
	}
	//2.将登录的结果发送给客户端
	send(newsockfd, &s, sizeof(s), 0);
}


void getSystemTime(char *p)
{
	time_t t;
	time(&t);
	struct tm *tp = localtime(&t);
	sprintf(p, "%d-%02d-%02d %02d:%02d:%02d",tp->tm_year+1900, tp->tm_mon+1, tp->tm_mday,
			tp->tm_hour, tp->tm_min,tp->tm_sec);

}


//接收到上传请求

void fileTransfer( int newsockfd,data_t s)
{
	char sql[200] = { 0 };
	
	char sysTime[50] = { 0 };
    static int flag = 1;
    static int fd;
    char pathName[100] = { 0 };
    
    // 1.打开文件
    sprintf(pathName, "./share/%s/%s", s.username, s.filename);
    
    if(flag)
    {
        flag = 0;
        fd = open(pathName, O_WRONLY | O_CREAT | O_TRUNC, 0666);  // 修正了标志位
        if(fd == -1)
        {
            perror("open failed");
            return;
        }
    }
    
    if(s.len == -1)  // 修正为 s.len
    {
        close(fd);

		getSystemTime(sysTime);
		sprintf(sql,"insert into his_info values('%s','%s','download %s')",s.username,sysTime,s.filename);

		mysqlite3_exec(sql);
        flag = 1;  // 重置标志位
        return;
    }
    
    // 2.将接收到的数据写入文件
    write(fd, s.filedata, s.len);  // 修正为 s.len
	printf("写入成功  写入了%d字节\n",s.len);
}


void dealFileList(int newsockfd,data_t s)
{
	char sql[200] ={ 0 };
	struct dirent *ep = NULL;
	char pathName[100] = { 0 };
	sprintf(pathName,"./share/%s",s.username);
	DIR * dp = opendir(pathName);

	if(dp ==NULL)
	{
		perror("opendir failed");
		return;
	}
	while((ep = readdir(dp)) != NULL)
	{
		if(ep->d_name[0] != '.')
		{	
			s.type = 3;
			strcpy(s.filename,ep->d_name);
			send(newsockfd,&s,sizeof(s),0);

		}

	}
	s.type = 0;
	send(newsockfd,&s,sizeof(s),0);
	closedir(dp);
}
void getDown(int newsockfd,data_t s)
{
	char sql[200];
	char sysTime[100];
	char pathName[100] = { 0 };//用来保存绝对路径
	sprintf(pathName,"./share/%s/%s",s.username,s.filename);
	//1.打开文件
	int fd = open(pathName, O_RDONLY);
	if(fd == -1)
	{
		perror("open failed");
		s.len = -1;//-1下载的文件不存在
		send(newsockfd, &s, sizeof(s), 0);
		return;
	}
	s.len = 0;//0代表下载的文件存在
	send(newsockfd, &s, sizeof(s), 0);
	
	//2.循环读取文件内容，读取一次发送一次
	while((s.len=read(fd, s.filedata, sizeof(s.filedata))) > 0)
	{
		send(newsockfd, &s, sizeof(s), 0);
	}
	//额外再多发送一次
	s.len = -2;//代表全部发送完毕
	send(newsockfd, &s, sizeof(s), 0);
	close(fd);
	//将下载记录保存到数据库中
	getSystemTime(sysTime);
	sprintf(sql,"insert into his_info values('%s','%s','download %s')",s.username,sysTime,s.filename);
	mysqlite3_exec(sql);


}
void dealHistoryInfo(int newsockfd,data_t s)
{
	char sql[200] = { 0 };                                                                                                                              
	char **resultp  = NULL;
	int row = 0,column,i;
	char *errmsg = NULL;
	sprintf(sql,"select *from his_info where username = '%s',s.username",s.username);
	int ret = sqlite3_get_table(db,sql,&resultp,&row,&column,&errmsg);
	if(ret != 0)
	{
		printf("sqlite3_get_table failed%s\n",errmsg);
		exit(-1);
	}
	for(i = 0; i < (row+1)*column; i+= column)
	{
		sprintf(s.filedata,"%-30s  %-30s   %-30s ",resultp[i],resultp[i+1],resultp[i+2]);
		s.type = 0;
		send(newsockfd,&s,sizeof(s),0);

	}
	s.type = -1;
	send(newsockfd,&s,sizeof(s),0);
}
void * doClientThread(void *p)
{
	int newsockfd =*(int *)p;
	data_t s = { 0 };
	sqlite3_create(); 
	while(1)
	{
		int ret = recv(newsockfd,&s,sizeof(s),0);
		if(ret <= 0)
		{
			printf("newsockfd %d 对应的客户段断开连接\n",newsockfd);
			close(newsockfd);
			return;
		}
		printf("接收到的请求类型:%d 用户名：%s  密码：%s\n",s.type,s.username,s.passwd);

		//判断客户端的请求类型，来做不同的处理
		switch(s.type)
		{	
			case 1: //注册请求
				dealRegister(s, newsockfd);
				break;
			case 2: //登录请求
				login(newsockfd,s);
				break;
			case 3:  //获取列表请求
				dealFileList(newsockfd,s);
				break;
			case 4://上传文件请求
				fileTransfer(newsockfd, s);
				break;
			case 5://下载请求
				getDown(newsockfd,s);
				break;
			case 6:
				dealHistoryInfo(newsockfd,s);
				break;
		}
	}
}

int main(int argc, const char *argv[])
{
	pthread_t id;
	int newsockfd;
	int sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd == -1)
	{
		perror("sockfd failed");
		exit(-1);
	}
	struct sockaddr_in myaddr = { 0 };
	myaddr.sin_family = AF_INET;
	myaddr.sin_port = htons(6666);
	myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	int ret = bind(sockfd,(struct sockaddr*)&myaddr,sizeof(myaddr));
	if(ret == -1)
	{
		perror("bind failed");
		exit(-1);
	}
	listen(sockfd,5);
	while(1)
	{
		newsockfd = accept(sockfd,NULL,NULL);
		printf("新的客户端连接成功，对应的newsockfd is %d\n",newsockfd);
		pthread_create(&id,NULL,doClientThread,&newsockfd);
	}
	return 0;
}
